package jp.co.nes.awssupport.report;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jp.co.nes.awssupport.access.dao.SforceManageDao;
import jp.co.nes.awssupport.common.bean.AmountReportBean;
import jp.co.nes.awssupport.common.bean.ContractInformationBean;
import jp.co.nes.awssupport.common.utility.AppInfoUtil;
import jp.co.nes.awssupport.report.constant.ReportType;
import jp.co.nes.awssupport.report.utility.ReportHelper;

/**
 * 料金レポートを出力するクラスで使用する抽象クラスです。
 */
public abstract class AbstractAmountReportClient extends AbstractReportClient {

    /** ダブルクオーテーション */
    protected static final String DQ = "\"";

    /** カンマ */
    protected static final String CONNMA = ",";

    /** 改行文字(\r\n) */
    protected static final String LINEFEED = "\r\n";

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(AbstractAmountReportClient.class);

    /** s3にアップロードする際に使用するバケット名 */
    private String bucketName;

    /** s3にアップロードする際に使用するフォルダ名(最後に/は不要)(%1$s:処理対象年月(uuuuMM)) */
    private String folderName;

    /** レポートを出力するディレクトリ(最後にセパレータは不要)(%1$s:処理対象年月(uuuuMM)) */
    private String csvOutputDirPath;

    /** レポートファイル名(%1$s:連結アカウントID %2$s:処理対象年月(uuuuMM)) */
    private String csvFileName;

    /** レポートを出力する際の文字コード */
    private String csvCharsetName;

    /** ポータルサイトに表示する内容(リンク名)(%1$s:対象年月(uuuuMM) %2$s:有効期限) */
    private String linkCsvTitle;

    /**
     * デフォルトコンストラクタです。
     */
    protected AbstractAmountReportClient() {
    }

    /**
     * レポートヘルパーを引数に持つコンストラクタです。
     *
     * @param reportHelper レポートヘルパー
     */
    protected AbstractAmountReportClient(ReportHelper reportHelper) {
        super(reportHelper);
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#getReportType()
     */
    @Override
    protected ReportType getReportType() {
        return ReportType.Csv;
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#getBucketName()
     */
    @Override
    protected String getBucketName() {
        return bucketName;
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#getProcessedFolderName()
     */
    @Override
    protected String getFolderName() {
        return folderName;
    }

    /**
     * レポートを出力するディレクトリ(最後にセパレータは不要)(%1$s:処理対象年月(uuuuMM))を返却します。
     *
     * @return レポートを出力するディレクトリ(%1$s:処理対象年月(uuuuMM))
     */
    private String getCsvOutputDirPath() {
        return csvOutputDirPath;
    }

    /**
     * レポートファイル名(%1$s:連結アカウントID %2$s:処理対象年月(uuuuMM))を返却します。
     *
     * @return レポートファイル名(%1$s:連結アカウントID %2$s:処理対象年月(uuuuMM))
     */
    private String getCsvFileName() {
        return csvFileName;
    }

    /**
     * レポートを出力する際の文字コードを返却します。
     *
     * @return レポートを出力する際の文字コード
     */
    private String getCsvCharsetName() {
        return csvCharsetName;
    }

    /**
     * ポータルサイトに表示する内容(リンク名)(%1$s:対象年月(uuuuMM) %2$s:有効期限)を返却します。
     *
     * @return ポータルサイトに表示する内容(リンク名)(%1$s:対象年月(uuuuMM) %2$s:有効期限)
     */
    private String getLinkCsvTitle() {
        return linkCsvTitle;
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#getDispReportName(java.lang.String)
     */
    @Override
    protected String getDispReportName(String objectKey) {
        return getLinkCsvTitle();
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#initialize()
     */
    @Override
    protected void initialize() {
        super.initialize();
        String category = getCategoryForAppInfo();
        bucketName = AppInfoUtil.getValAndEmptyChk(category, "BucketName");
        folderName = AppInfoUtil.getValAndEmptyChk(category, "FolderName");
        csvOutputDirPath = AppInfoUtil.getValAndEmptyChk(category, "CsvOutputDirPath");
        csvFileName = AppInfoUtil.getValAndEmptyChk(category, "CsvFileName");
        csvCharsetName = AppInfoUtil.getVal(category, "CsvCharsetName", "UTF-8");
        linkCsvTitle = AppInfoUtil.getValAndEmptyChk(category, "LinkCsvTitle");
    }

    /**
     * アプリケーション情報のカテゴリを返却します。
     *
     * @return アプリケーション情報のカテゴリ
     */
    protected String getCategoryForAppInfo() {
        String simpleName = getSimpleName();
        int index = simpleName.indexOf("Client");
        return simpleName.substring(0, index);
    }

    /**
     * 実行クラスのクラス名(パッケージを抜いたクラス名)を返却します。
     *
     * @return 実行クラスのクラス名
     */
    protected String getSimpleName() {
        return getClass().getSimpleName();
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#searchContractInformationList(java.util.List)
     */
    @Override
    protected List<ContractInformationBean> searchContractInformationList(List<String> idList) {
        return SforceManageDao.getContractInformationList_AmountReport(idList, getTargetYearMonth());
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.report.creating.AbstractReportClient#createReport(java.lang.String, jp.co.nes.awssupport.access.bean.ContractInformationBean)
     */
    @Override
    protected boolean createReport(String targetId, ContractInformationBean ciBean) {
        if (logger.isDebugEnabled()) {
            logger.debug(toMessage(targetId, "createReport start"));
        }

        // 連結アカウントIDのチェック
        if (StringUtils.isEmpty(targetId)) {
            logger.error(toMessage("サポートポータルから連結アカウントIDが取得できませんでした。"));
            return false;
        }

        List<AmountReportBean> reportDetailList = searchReportDetail(targetId);
        if (logger.isDebugEnabled()) {
            int size = 0;
            if (reportDetailList != null) {
                size = reportDetailList.size();
            }
            StringBuilder builder = new StringBuilder();
            builder.append("データ取得件数 ");
            builder.append(Integer.toString(size));
            builder.append(" 件");
            logger.debug(toMessage(targetId, builder.toString()));
        }
        if (reportDetailList != null && reportDetailList.isEmpty()) {
            logger.warn(toMessage(targetId, "料金データが存在しません。"));
        }

        // ヘッダ行をビルダーに追加
        StringBuilder csvBuilder = new StringBuilder("");
        csvBuilder.setLength(0);
        csvBuilder.append(createReportHeader());

        for (AmountReportBean reportDetail : reportDetailList) {
            // 行データを取得し、ビルダーに追加
            String line = createReportLine(reportDetail);
            if (logger.isDebugEnabled()) {
                String debugLine = null;
                if (line != null) {
                    // 改行を削除してデバッグ文を出力する。
                    debugLine = line.replace(LINEFEED, "");
                }
                logger.debug(toMessage(targetId, "出力行データ：" + debugLine));
            }
            csvBuilder.append(line);
        }

        boolean isError = false;
        try {
            addCsvReport(writeReportFile(targetId, csvBuilder.toString()));
        } catch (IOException e) {
            // 例外が発生した場合は、エラーログを出力し、処理を続行する。
            isError = true;
            StringBuilder builder = new StringBuilder();
            builder.append("ファイル出力処理でエラーが発生しました。\n ");
            builder.append(e.getMessage());
            logger.error(toMessage(targetId, builder.toString()), e);
        }

        return !isError;
    }

    /**
     * レポートを出力する際に使用するデータを返却します。<br>
     * データが存在しなかった場合は、空リスト返却します。
     *
     * @param targetId 処理対象の連結アカウントID
     * @return レポートを出力する際に使用するデータ
     */
    protected abstract List<AmountReportBean> searchReportDetail(String targetId);

    /**
     * レポートのヘッダ行を作成します。
     *
     * @return レポートのヘッダ行
     */
    protected abstract String createReportHeader();

    /**
     * レポートの明細行を作成します。
     *
     * @param bean 料金レポートBean
     * @return レポートの明細行
     */
    protected abstract String createReportLine(AmountReportBean reportDetail);

    /**
     * CSVファイルを書き込みます。
     *
     * @param targetId 処理対象の連結アカウントID
     * @param outputText CSVに書き込む内容
     * @return CSVファイルオブジェクト
     * @throws FileNotFoundException ファイルが見つからない場合にスローします。
     * @throws IOException IOエラーが発生した場合にスローします。
     */
    private File writeReportFile(String targetId, String outputText) throws FileNotFoundException, IOException {
        String outputFileName = createOutputFileName(targetId, getCsvOutputDirPath(), getCsvFileName());

        if (logger.isDebugEnabled()) {
            logger.debug(toMessage(targetId, "<<writeFile>>fileName：" + outputFileName));
        }

        File csvFile = new File(outputFileName);
        try (FileOutputStream outputStream = new FileOutputStream(csvFile)) {
            outputStream.write(outputText.getBytes(getCsvCharsetName()));
            return csvFile;
        }
    }
}
